import json
import time
from typing import Dict, Any, List, Callable
from dataclasses import dataclass

from playwright.sync_api import Page, Request, Response

from .event_stream_js import JSEventStreamInterceptor


@dataclass
class NetworkData:
    url: str
    method: str

    request_headers: Dict[str, str]
    request_body: Any

    status_code: int = 0
    response_headers: Dict[str, str] = None
    response_event_stream: List[Dict[str, str]] = None
    response_body: Any = None


class NetworkWatcher:
    def __init__(self, page: Page):
        self.page = page
        self.networks: Dict[str, List[NetworkData]] = {}
        self.networks_timeline: List[NetworkData] = []
        self._setup_listeners()

    def setup_js_interceptor(self):
        self.page.evaluate(JSEventStreamInterceptor)
        pass

    def _setup_listeners(self):
        self.page.on("request", self._on_request)
        self.page.on("response", self._on_response)
        pass

    def _append_to_timeline(self, request: Request, response: Response = None):
        if not self.networks.get(request.url):
            self.networks[request.url] = []
            pass

        if response:
            for network_data in self.networks[request.url]:
                if network_data.method == request.method and network_data.status_code == 0:
                    network_data.status_code = response.status
                    network_data.response_headers = dict(response.headers)
                    network_data.response_event_stream = self._detect_response_event_stream(response)

                    content_type = response.headers.get('content-type', '').lower()
                    if 'text' in content_type or 'json' in content_type or 'xml' in content_type:
                        # 尝试用不同方式获取响应体
                        try:
                            body_text = response.text()
                        except Exception:
                            try:
                                body_text = response.body().decode('utf-8')
                            except Exception:
                                body_text = None

                        network_data.response_body = body_text
                    else:
                        network_data.response_body = None
                    return
                pass
            pass

        network_data = NetworkData(
            url=request.url,
            method=request.method,
            request_headers=dict(request.headers),
            request_body=request.post_data,
        )
        self.networks[request.url].append(network_data)
        self.networks_timeline.append(network_data)
        pass

    def _on_request(self, request: Request):
        self._append_to_timeline(request=request)
        pass

    def _detect_response_event_stream(self, response: Response):
        if 'text/event-stream' not in response.headers.get('content-type', ''):
            return ''

        trace_key = ''
        for key in response.headers.keys():
            if 'trace' in key:
                trace_key = key
                pass
            pass

        trace_id = response.headers.get(trace_key)
        if not trace_id:
            return ''

        trace_el = self.page.wait_for_selector(f'#trace_id_{trace_id}')
        text_content = trace_el.text_content() or ''
        print('text_content', text_content)
        if not text_content:
            return ''

        return json.loads(text_content)

    def _on_response(self, response: Response):
        request = response.request
        self._append_to_timeline(request=request, response=response)
        pass

    def watch(self, url_pattern: str, filters: Callable[[NetworkData], bool] = None):
        def get_network_data():
            for url in self.networks.keys():
                if url_pattern not in url: continue
                if filters is None:
                    return self.networks[url]

                if not filters:
                    return [
                        network_data
                        for network_data in  self.networks[url]
                        if filter_response(network_data)
                    ]

                return [
                    network_data
                    for network_data in self.networks[url]
                    if filters(network_data) and filter_response(network_data)
                ]
            return None

        result = get_network_data()

        print('result', result)

        if result:
            return result

        self.page.wait_for_selector('[id*=trace_id]')

        print('111111111111111111111111111111111111111111111')

        return get_network_data()


def filter_response(data: NetworkData):
    return data.status_code > 0
